package com.block.match.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.block.common.enums.InfoTypeEnum;
import com.block.common.enums.ProfitSharingEnum;
import com.block.common.exception.NotifyExceptionService;
import com.block.common.model.dto.RequestTimeDTO;
import com.block.common.service.IMerchantCommonService;
import com.block.common.service.ISystemRequestTimeRecordService;
import com.block.common.service.LogFactory;
import com.block.datapush.model.result.FilterHandlerResult;
import com.block.datapush.service.IDataFilterHandleService;
import com.block.db.entity.*;
import com.block.db.mapper.MerchantTaskInfoMapper;
import com.block.match.enums.OrderStatusEnum;
import com.block.match.model.dto.SourceMatchResultDTO;
import com.block.match.model.dto.SourceRegisterResultDTO;
import com.block.match.service.IDataMatchOrderInfoService;
import com.block.match.service.IDataMatchOrderItemService;
import com.block.match.service.ISourceMatchHandleService;
import com.block.merchant.enums.CheckStatusEnum;
import com.block.merchant.model.dto.CheckSourceTypeDTO;
import com.block.merchant.model.dto.MerchantOrderDTO;
import com.block.merchant.model.result.DataPushResult;
import com.block.merchant.model.result.MatchMerchantInfo;
import com.block.merchant.service.IMerchantBaseInfoService;
import com.block.merchant.service.IMerchantDataPushInfoService;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class SourceMatchHandleService implements ISourceMatchHandleService {

    private static final Logger log = LogFactory.getSourceLog();
    private static final Logger logTime = LogFactory.getTimeLog();

    @Resource
    MerchantTaskInfoMapper merchantTaskInfoMapper;
    @Autowired
    IDataFilterHandleService dataFilterHandleService;
    @Autowired
    NotifyExceptionService notifyExceptionService;
    @Autowired
    IMerchantBaseInfoService merchantBaseInfoService;
    @Autowired
    IDataMatchOrderInfoService dataMatchOrderInfoService;
    @Autowired
    IDataMatchOrderItemService dataMatchOrderItemService;
    @Autowired
    IMerchantDataPushInfoService merchantDataPushInfoService;
    @Autowired
    ISystemRequestTimeRecordService systemRequestTimeRecordService;
    @Autowired
    IMerchantCommonService merchantCommonService;
    /**
     * 临时数据撞库
     */
    @Override
    public SourceMatchResultDTO toMatchMerchant(SourceBaseInfo sourceBaseInfo, SourceMemberDataInfoTemp dataInfoTemp,CheckSourceTypeDTO checkSourceTypeDTO) throws InterruptedException {

        // dataInfoTemp 转 memberSourceInfo
        MemberSourceInfo memberSourceInfo = new MemberSourceInfo();
        BeanUtil.copyProperties(dataInfoTemp, memberSourceInfo);
        memberSourceInfo.setReqIp(dataInfoTemp.getUserIp());
        memberSourceInfo.setIdcard(dataInfoTemp.getIdCard());
        memberSourceInfo.setRealname(dataInfoTemp.getRealName());
        memberSourceInfo.setChannelCode(dataInfoTemp.getChannelCode());

        List<MerchantTaskInfo> resultTaskList = new ArrayList<>();
        Set<Integer> canMatchMerchantSet = new HashSet<>();// 过滤同一个商户的作用

        //  2023/12/27 根据 sourceBaseInfo
        // priceAmount priceRate 计算起推价格
        BigDecimal startPriceAmount = sourceBaseInfo.getPriceAmount().multiply(sourceBaseInfo.getPriceRate());

        Long time0 = System.currentTimeMillis();

        // 查询所有开启的商户任务
        List<MerchantTaskInfo> taskInfoList = merchantTaskInfoMapper.getAllEnableTask();

        taskInfoList = taskInfoList.stream()
                .filter(o1 -> {
                    // 获取 merchantBaseInfo 对象
                    MerchantBaseInfo merchantBaseInfo = merchantCommonService.getMerchantBaseInfo(o1.getInfoId());

                    // 获取 checkType
                    String checkType = merchantBaseInfo.getCheckType();

                    if (ObjectUtil.equal(CheckStatusEnum.MOBILEMD5.getValue(), checkSourceTypeDTO.getCheckType())) {
                        // 如果上游是MD5撞库，下游也是MD5撞库
                        return ObjectUtil.equal(CheckStatusEnum.MOBILEMD5.getValue(), checkType) ||
                                StrUtil.equals(InfoTypeEnum.CRM.getValue(),o1.getInfoType());
                    } else if (ObjectUtil.equal(CheckStatusEnum.MOBILEMASK.getValue(), checkSourceTypeDTO.getCheckType())) {
                        // 如果上游是掩码撞库，下游也是掩码撞库
                        return ObjectUtil.equal(CheckStatusEnum.MOBILEMASK.getValue(), checkType) ||
                                StrUtil.equals(InfoTypeEnum.CRM.getValue(), o1.getInfoType());
                    }
                    return false;
                }).collect(Collectors.toList());

        if (ProfitSharingEnum.FIXEDPRICE.getValue().equals(sourceBaseInfo.getPriceType())) {
            //固定价格
            taskInfoList =
                    taskInfoList.stream()
                            // 起推价格过滤; 起推价格 < 任务价格
                            .filter(o1 -> startPriceAmount.compareTo(o1.getPriceAmount()) <= 0)
                            // 不能推给apib
                            .filter(o1 -> !StrUtil.equals(InfoTypeEnum.APIB.getValue(), o1.getInfoType()))
                            // 不能推给 分润机构
                            .filter(o1 -> StrUtil.equals(ProfitSharingEnum.FIXEDPRICE.getValue(), o1.getPriceType()))
                            // 价格倒序
                            .sorted(Comparator.comparing(MerchantTaskInfo::getPriceAmount).reversed())
                            .collect(Collectors.toList());
        } else if (ProfitSharingEnum.PROFITPRICE.getValue().equals(sourceBaseInfo.getPriceType())) {


            if (StrUtil.equals(sourceBaseInfo.getChannelCode(), "ZIUFGV")){
                //分润价格
                taskInfoList =
                        taskInfoList.stream()
                                // 起推价格过滤; 起推价格 < 任务价格
//                            .filter(o1 -> startPriceAmount.compareTo(o1.getPriceAmount()) <= 0)
                                // 机构类型过滤；分润渠道可以推送分润机构（不能推给apib）
                                .filter(o1 -> !StrUtil.equals(InfoTypeEnum.APIB.getValue(), o1.getInfoType()))
                                .filter(o1 -> (StrUtil.contains(o1.getCityInfo(), "9999") &&
                                        (StrUtil.equals(InfoTypeEnum.CRM.getValue(), o1.getInfoType()) || StrUtil.equals(InfoTypeEnum.API.getValue(), o1.getInfoType()))))
                                // 价格倒序
                                .sorted(Comparator.comparing(MerchantTaskInfo::getPriceAmount).reversed())
                                .collect(Collectors.toList());
            }else{
                //分润价格
                taskInfoList =
                        taskInfoList.stream()
                                // 起推价格过滤; 起推价格 < 任务价格
//                            .filter(o1 -> startPriceAmount.compareTo(o1.getPriceAmount()) <= 0)
                                // 机构类型过滤；分润渠道可以推送分润机构（不能推给apib）
                                .filter(o1 -> !StrUtil.equals(InfoTypeEnum.APIB.getValue(), o1.getInfoType()))
                                // 价格倒序
                                .sorted(Comparator.comparing(MerchantTaskInfo::getPriceAmount).reversed())
                                .collect(Collectors.toList());
            }
        }

        // 数据库查询可推送任务
        Long time1 = System.currentTimeMillis();

        Long execTime1 = time1 - time0;
        if (execTime1 > 500) {
            RequestTimeDTO timeDTO = new RequestTimeDTO();
            timeDTO.setBizType("MERCHANT_CHECK_DATA_SELECT");
            timeDTO.setBizName("机构撞库流程数据查询");
            timeDTO.setExecTime(String.valueOf(execTime1));
            timeDTO.setLineTime("0");
            timeDTO.setParamInfo(StrUtil.format("【startPriceAmount】{}【infoType】{}", startPriceAmount, sourceBaseInfo.getType()));
            systemRequestTimeRecordService.saveRequestTime(timeDTO);
        }

        // 机构任务过滤，筛选可推送的任务
        for (MerchantTaskInfo merchantTaskInfo : taskInfoList) {
            try {
                FilterHandlerResult filterResult = dataFilterHandleService.execMerchantFilter(merchantTaskInfo, memberSourceInfo);
                log.info("【商户任务过滤】过滤结束【taskId】{}【uid】{}【filterResult】{}",
                        merchantTaskInfo.getId(), memberSourceInfo.getUid(), JSON.toJSONString(filterResult));

                // 过滤成功，且拿到价格最高的推送任务（同一个商户）
                if (filterResult.isSuccess() &&
                        !canMatchMerchantSet.contains(merchantTaskInfo.getInfoId())) {

                    canMatchMerchantSet.add(merchantTaskInfo.getInfoId());
                    resultTaskList.add(merchantTaskInfo);
                }
            } catch (Exception exception) {
                exception.printStackTrace();
                String message = StrUtil.format("【uid】:{}；【taskId】:{};【infoId】:{};",
                        memberSourceInfo.getUid(), merchantTaskInfo.getId(), merchantTaskInfo.getInfoId());
                notifyExceptionService.notifyNoticeInfo("MERCHANT1", "商户任务过滤异常1", message, exception);
            }
        }

        // 筛选满足条件的任务
        Long time2 = System.currentTimeMillis();

        // TODO: 2024/1/8 撞库过程异步执行
        MatchMerchantInfo resultInfo = null;

        CountDownLatch countDownLatch = new CountDownLatch(resultTaskList.size());

        // TODO: 2024/1/8 resultTaskList 并发执行
        List<Future<MatchMerchantInfo>> futureList = new ArrayList<>();
        for (MerchantTaskInfo merchantTaskInfo : resultTaskList) {
            try {
                MerchantOrderDTO merchantOrderDTO = new MerchantOrderDTO();
                merchantOrderDTO.setInfoId(merchantTaskInfo.getInfoId());
                merchantOrderDTO.setTaskId(merchantTaskInfo.getId());
                merchantOrderDTO.setPriceAmount(merchantTaskInfo.getPriceAmount());
                merchantOrderDTO.setCostAmount(merchantTaskInfo.getCostAmount());
                merchantOrderDTO.setPayAmount(merchantTaskInfo.getPayAmount());

                // 根据可推送的任务，进行撞库等操作
                // 先进行撞库，然后进行推送
                // 推送的时候更新订单的信息

                Future<MatchMerchantInfo> merchantInfoFuture = ThreadUtil.execAsync(new Callable<MatchMerchantInfo>() {
                    @Override
                    public MatchMerchantInfo call() throws Exception {
                        checkSourceTypeDTO.setMemberSourceInfo(memberSourceInfo);
                        Long start0 = System.currentTimeMillis();
                        MatchMerchantInfo matchMerchantInfo = merchantBaseInfoService.sourceCheckWithMerchant(merchantOrderDTO, checkSourceTypeDTO);
                        Long end0 = System.currentTimeMillis();
                        long execTime = end0 - start0;
                        if (execTime > 2000) {
//                            logTime.info("【机构撞库】机构撞库完成【execTime】{}【infoId】{}", execTime, merchantTaskInfo.getInfoId());
                            RequestTimeDTO timeDTO = new RequestTimeDTO();
                            timeDTO.setBizType("MERCHANT_CHECK_HANDLE");
                            timeDTO.setBizName("机构撞库流程");
                            timeDTO.setExecTime(String.valueOf(execTime));
                            timeDTO.setLineTime("1000");
                            timeDTO.setParamInfo(StrUtil.format("【infoId】{}", merchantTaskInfo.getInfoId()));
                            timeDTO.setResultInfo(JSON.toJSONString(matchMerchantInfo));
                            timeDTO.setRemarkInfo(StrUtil.format("【resultNo】{}", dataInfoTemp.getId()));
                            systemRequestTimeRecordService.saveRequestTime(timeDTO);
                        }

                        countDownLatch.countDown();
                        return matchMerchantInfo;
                    }
                });

                futureList.add(merchantInfoFuture);
                // 只要一个撞库结果
                // 根据排序结果，拿到最终的排序结果
//                if (matchMerchantInfo.isMatchSuccess()){
//                    resultInfo = matchMerchantInfo;
//                    break;
//                }
            } catch (Exception exception) {
                exception.printStackTrace();
                String message = StrUtil.format("【uid】:{}；【taskId】:{};【infoId】:{};",
                        memberSourceInfo.getUid(), merchantTaskInfo.getId(), merchantTaskInfo.getInfoId());
                notifyExceptionService.notifyNoticeInfo("MERCHANT", "商户任务推送异常", message, exception);
            }
        }

        // 系统等待时间
        countDownLatch.await(4, TimeUnit.SECONDS);

        // 异步执行 可推送任务的请求
        Long time3 = System.currentTimeMillis();

        for (Future<MatchMerchantInfo> merchantInfoFuture : futureList) {
            if (merchantInfoFuture.isDone()) {
                try {
                    MatchMerchantInfo merchantInfo = merchantInfoFuture.get();
                    if (merchantInfo.isMatchSuccess()) {
                        resultInfo = merchantInfo;
                        break;
                    }
                } catch (Exception exception) {
                    exception.printStackTrace();
                    String message = StrUtil.format("【uid】:{}；", memberSourceInfo.getUid());
                    notifyExceptionService.notifyNoticeInfo("MERCHANT_EXEC", "商户任务推送异步异常", message, exception);
                }
            }
        }

        SourceMatchResultDTO resultDTO = new SourceMatchResultDTO();
        resultDTO.setSuccess(false);

        // 获取到撞库完成的任务
        Long time4 = System.currentTimeMillis();

        if (ObjectUtil.isNotEmpty(resultInfo)) {
            // 撞库通过，生成订单信息
            DataMatchOrderInfo matchOrderInfo = dataMatchOrderInfoService.initMatchOrderInfo(memberSourceInfo);
            DataMatchOrderItem matchOrderItem = dataMatchOrderItemService.initMatchOrderItem(resultInfo, matchOrderInfo, memberSourceInfo);

            resultDTO.setSuccess(resultInfo.isMatchSuccess());
            resultDTO.setOrderNo(String.valueOf(matchOrderInfo.getId()));
            resultDTO.setOrderItemNo(String.valueOf(matchOrderItem.getId()));
            resultDTO.setInfoId(resultInfo.getInfoId());
            resultDTO.setInfoName(resultInfo.getInfoName());
            resultDTO.setInfoType(resultInfo.getInfoType());
            resultDTO.setTaskId(resultInfo.getTaskId());
            resultDTO.setPriceAmount(resultInfo.getProfitAmount());
            resultDTO.setPushId(resultInfo.getPushId());
            resultDTO.setMd5List(resultInfo.getMd5List());

        }
        // 获取到撞库成功的任务
        Long time5 = System.currentTimeMillis();

        if (time5 - time0 > 2000) {
            RequestTimeDTO timeDTO = new RequestTimeDTO();
            timeDTO.setBizType("MERCHANT_CHECK_RECORD");
            timeDTO.setBizName("机构撞库流程分步记录");
            timeDTO.setExecTime(String.valueOf(time5 - time0));
            timeDTO.setLineTime("0");
            timeDTO.setParamInfo(StrUtil.format("【channelCode】{}【channelName】{}【mobileMd5】{}", sourceBaseInfo.getChannelCode(), sourceBaseInfo.getChannelName(), memberSourceInfo.getMobileMd5()));
//        timeDTO.setResultInfo(JSON.toJSONString(matchMerchantInfo));
            timeDTO.setRemarkInfo(StrUtil.format("【time1-time0】{}【time2-time1】{}【time3-time2】{}【time4-time3】{}【time5-time4】{}",
                    time1 - time0, time2 - time1, time3 - time2, time4 - time3, time5 - time4));

            systemRequestTimeRecordService.saveRequestTime(timeDTO);
        }

        return resultDTO;
    }

    /**
     * 手动点击订单推送
     *
     * @param resultDTO
     * @return
     * @throws Exception
     */
    @Override
    public SourceRegisterResultDTO toPushMerchant(SourceMatchResultDTO resultDTO) throws Exception {

        SourceRegisterResultDTO registerResultDTO = new SourceRegisterResultDTO();
        registerResultDTO.setSuccess(false);

        Long time0 = System.currentTimeMillis();

        DataMatchOrderItem matchOrderItem = dataMatchOrderItemService.getById(resultDTO.getOrderItemNo());
        if (StrUtil.equals(matchOrderItem.getInfoType(), "APIB")) {
            log.info("【机构点击推送】APIB类型机构，不做推送处理【resultDTO】{}", JSON.toJSONString(resultDTO));
            registerResultDTO.setMessage("APIB机构不推送");
            return registerResultDTO;
        }

        Long time1 = System.currentTimeMillis();

        //订单推送
        DataPushResult matchMerchantInfo = merchantBaseInfoService.pushToMerchant(matchOrderItem);
        // 更新订单推送结果
        log.info("【机构点击推送】机构推送结束【resultDTO】{}【matchMerchantInfo】{}", JSON.toJSONString(resultDTO), JSON.toJSONString(matchMerchantInfo));

        Long time2 = System.currentTimeMillis();

        DataMatchOrderItem updateOrderItem = new DataMatchOrderItem();
        updateOrderItem.setId(matchOrderItem.getId());

        DataMatchOrderInfo updateOrderInfo = new DataMatchOrderInfo();
        updateOrderInfo.setId(Long.valueOf(resultDTO.getOrderNo()));

        if (matchMerchantInfo.isPushSuccess() && matchMerchantInfo.isSettleFlag()) {
            updateOrderInfo.setStatus(OrderStatusEnum.SUCCESS.getValue());
            updateOrderItem.setStatus(OrderStatusEnum.SUCCESS.getValue());

            registerResultDTO.setSuccess(true);
            registerResultDTO.setSettleFlag(true);
            registerResultDTO.setMessage(matchMerchantInfo.getMessage());
        } else {
            updateOrderInfo.setStatus(OrderStatusEnum.FAILED.getValue());
            updateOrderItem.setStatus(OrderStatusEnum.FAILED.getValue());
            registerResultDTO.setMessage(matchMerchantInfo.getMessage());
        }

        Long time3 = System.currentTimeMillis();

        if (time3 - time0 > 2500) {
            RequestTimeDTO timeDTO = new RequestTimeDTO();
            timeDTO.setBizType("MERCHANT_PUSH_USERINFO");
            timeDTO.setBizName("商户推送时间记录");
            timeDTO.setExecTime(String.valueOf(time3 - time0));
            timeDTO.setParamInfo(StrUtil.format("【resultDTO】{}", JSON.toJSONString(resultDTO)));
            timeDTO.setResultInfo(JSON.toJSONString(resultDTO));
            timeDTO.setRemarkInfo(StrUtil.format("【time3-time2】{}【time2-time1】{}【time1-time0】{}",
                    time3 - time2, time2 - time1, time1 - time0));
            systemRequestTimeRecordService.saveRequestTime(timeDTO);
        }

        return registerResultDTO;
    }

    /**
     * 推送用户注册信息
     *
     * @param resultDTO
     * @return
     */
    @Override
    public SourceRegisterResultDTO handleRegisterUser(SourceBaseInfo sourceBaseInfo, SourceMatchResultDTO resultDTO, MemberSourceInfo memberSourceInfo) {

        SourceRegisterResultDTO registerResultDTO = new SourceRegisterResultDTO();
        registerResultDTO.setSuccess(true);
        try {

            DataMatchOrderInfo matchOrderInfo = dataMatchOrderInfoService.getById(resultDTO.getOrderNo());
            DataMatchOrderItem matchOrderItem = dataMatchOrderItemService.getById(resultDTO.getOrderItemNo());

            DataMatchOrderInfo updateOrder = new DataMatchOrderInfo();
            updateOrder.setId(matchOrderInfo.getId());
            updateOrder.setUid(memberSourceInfo.getUid());
            dataMatchOrderInfoService.updateById(updateOrder);

            DataMatchOrderItem updateOrderItem = new DataMatchOrderItem();
            updateOrderItem.setId(matchOrderItem.getId());
            updateOrderItem.setUid(memberSourceInfo.getUid());
            dataMatchOrderItemService.updateById(updateOrderItem);

            // 机构类型
            if (!StrUtil.equals(matchOrderItem.getInfoType(), InfoTypeEnum.CRM.getValue())) {
                MerchantDataPushInfo dataPushInfo = new MerchantDataPushInfo();
                dataPushInfo.setId(Long.valueOf(matchOrderItem.getPushId()));
                dataPushInfo.setUid(memberSourceInfo.getUid());
                dataPushInfo.setOrderId(matchOrderItem.getOrderId());
                dataPushInfo.setOrderItemId(matchOrderItem.getId());
                dataPushInfo.setExtData("非crm更新用户ID/订单号：" + sourceBaseInfo.getType());
                merchantDataPushInfoService.updateById(dataPushInfo);
            }

            return registerResultDTO;
        } catch (Exception exception) {
            exception.printStackTrace();
            String message = StrUtil.format("【resultDTO】{}", JSON.toJSONString(resultDTO));
            notifyExceptionService.notifyNoticeInfo("SOURCE_REGISTER_HANDLE", "注册操作异常", message, exception);
        }
        return registerResultDTO;
    }


}
